最近有两个星期的的课程设计 课题好多 选了一个自己能做出来的 那就是员工管理系统了
先设计简单的需求 写完再改 毕竟只有两个星期 要是写不完就尴尬了 其实肯定能写完 但是总该留点时间复习考试 自己得记性本来就不太好 还不好好的多看一下 哈哈

有了之前开发新闻发布系统的经验 这个员工管理系统的开发也就不算很难 如果要是扣细节的话 花费的时间估计也是不少 先简单的来写 后面的的在说 先实现员工和部门之间的关系 再实现员工添加 员工信息的修改 部门的添加 两个表我感觉足够了 先实现 后完善 是不是敏捷开发的思想(当然不仅仅这些) 哈哈
由于之前有了开发springboot的经验 自己这次搭建剩了不少力气 今天也就实现了框架的搭建 和 实体类 又温习了一遍springData
Repository模块中的
接口继承 CrudRepository<实体类,long>
Service模块中
依赖注入 所需要的Repository
本人强烈建议直接用 CrudRepository中的save()来进行insert操作
不建议用Query 和 Modifying

####写到一半自习室要关了 算了 回去写
当然别忘了 Transition注解 如果失败 可以事务回滚
当然我在一对多和一对一注解中的级联操作有了疑问
其中注解中有两个关键属性 cascade 和fetch
cascade 中有五个属性 ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH;
这个五个属性和删除更改操作有关
在员工对部门多对一的情况下
如果所关联的部门被删除 则当前员工外键setNull
如果所关联的部门发生更改 则当前员工也应该跟着修改
那就应该选择MEGER属性和 DETACH 不要选 All
注意的事项就这些
–完

今天下午准时收到了面试电话 心里非常开心 接下来分享一波面试问我的问题 面试官很和蔼 讲话也很好听 我也收益颇多 对于面试官的问题 我不喜欢用非常标准的套话来回答那个问题 也许我的回答很笼统 但是 我必须得明白 我回答的是什么 而不是仅仅把自己背的一些原理给面试官讲 那有什么用处呢?

接下来分享一波面试的问题

  1. 首先问的是项目的具体内容
  2. 了解jvm吗 说一下jvm的内存区分为那几块
  3. 讲一下hashmap
  4. 说一下关于spring的控制反转和依赖注入
  5. HashMap如何有序 天呐 当时瞬间懵逼了
    HashMap明明是无序的 不过我面试后上网查了一下 HashMap可以实现LinkedHashMap()这个类而进行迭代输出值 保证的是插入顺序 TreeMap是不允许key值和value值为null 不支持线程同步 HashMap支持一个为null的key 之后再有null的key会被覆盖
  6. 说一下树的遍历 二叉树的查找 二叉树的应用 html和xml的 dom树就是一个很好的例子 还有计算机文件夹目录就是很好的二叉树应用不过当时没有说出来 二分查找 冒泡排序算法思想

面试官给人的感觉很好 在面试过程中虽然也有一些发挥不太好 但是也收获了很多
–完

有些时候 如何使用一个框架和基础固然重要 编程语言好比一把剑 那么开发思想就是武功招式 剑固然重要 后期可以学习 更为重要的是对思想的理解 对开发模式的理解

敏捷开发不是一门具体的技术 而是一种理念 可以指导我们更加高效的开发
传统开发的模式例子有:瀑布流的开发模式

也就是从需求到设计,从设计到编码,从编码到测试,从测试到提交大概这样的流程,要求每一个开发阶段都要做到最好。特别是前期阶段,设计的越完美,提交后的成本损失就越少。一个阶段的输出是下一个阶段的输入 开发后期才能看到软件模样 没有迭代和反馈 不适合需求变更的较多的项目

但是敏捷开发就不一样:
敏捷开发集成了新型开发模式的共同特点,它重点强调:

1.敏捷就是“快”。快才可以适应目前社会的快节奏,要快就要发挥个人的个性思维多一些个性思维的增多。
2.客户参与。以人为本,客户是软件的使用者,是业务理解的专家,没有客户的参与,开发者很难理解客户的真实需求。
3.强调软件开发的产品是软件,而不是文档。文档是为软件开发服务的,而不是开发的主体。
4.设计周密是为了最终软件的质量,但不表明设计比实现更重要。
5.迭代。软件的功能是客户的需求,界面的操作是客户的“感觉”。对迭代的强调是缩短了软件版本的周期。
6.小版本。快速功能的展现,看似简单,但对于复杂的客户需求合理地分割与总体上的统一,要很好地二者兼顾是不容易的。

–end

又到了学期的末尾,马上要去实习 虽然还没有找到实习的地方 但是我相信 机会肯定会留给我的,顺便总结一波Hibernate基于4.3版本基于xml的总结干货
虽然我推荐使用Hibernate5.0即以上注解版本 没有xml的繁琐配置 送上一波Hibernate5.0的用户手册api

这次总结仅限学习 项目还是推荐使用5.0基于注解的版本 配置简单 使用方便 开箱即用

接下来开始讲解4.3的基于xml配置和xml映射文件的使用练习

  1. 从官网下载4.3的包 笔者用的的是Intellij的IDEA编译器 没用maven或者gradle包管理工具 只是纯属用于学习
  2. 在IDEA建立一个普通的JAVA工程导入下载的lib中的required的所有jar包 接下就开始开始配置
  3. 先送上一波官网关于4.3的用户手册
  4. 接下来在src中建立xml配置文件 建议命名为 hibernate.cfg.xml不过 在model层的映射文件推荐 类名.hbm.xml
  5. 配置文件怎么写
    <!DOCTYPE hibernate-configuration PUBLIC
         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
         "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
             <hibernate-configuration>
                 <session-factory>
                     <property name="show_sql">true</property>
                     <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
                     <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
                     <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/Hibernate</property>
                     <property name="hibernate.connection.username">root</property>
                     <property name="hibernate.connection.password">root</property>
                     <!-- 当前线程只对应一个Session实例 -->
                     <property name="hibernate.current_session_context_class">thread</property>
                     <!--四种属性 create update validate  create-drop-->
                     <property name="hbm2ddl.auto">update</property>
                 <mapping resource="Entity/user.xml"/>
             </session-factory>
         </hibernate-configuration>
    
上方四种属性的说明
    create:
    每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。

    create-drop :
    每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。

    update(推荐):
    最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据 model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等 应用第一次运行起来后才会。

    validate :
    每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

  1. Http协议头中的Referer主要用来让服务器判断来源页面, 即用户是从哪个页面来的,通常被网站用来统计用户来源,是从搜索页面来的,还是从其他网站链接过来,或是从书签等访问,以便网站合理定位.
  2. Referer有时也被用作防盗链, 即下载时判断来源地址是不是在网站域名之内, 否则就不能下载或显示,很多网站,如天涯就是通过Referer页面来判断用户是否能够下载图片.

在某些情况下,出于一些原因,想要控制页面发送给 server 的 referer 信息的情况下,可以使用这 referer metadata 参数
(也可以用h5属性a标签rel=”noreferer” 去掉refer)。

referer 的 metedata 参数可以设置为以下几种类型的值:

never  
always   
origin  
default  

如果在文档中插入 meta 标签,并且 name 属性的值为 referer,浏览器客户端将按照如下步骤处理这个标签:

 1. 如果 meta 标签中没有 content 属性,则终止下面所有操作
2. 将 content 的值复制给 referrer-policy,并转换为小写
3. 检查 content 的值是否为上面 list 中的一个,如果不是,则将值置为 default

上述步骤之后,浏览器后续发起 http 请求的时候,会按照 content 的值,做出如下反应(下面 referer-policy 的值即 meta 标签中 content 的值):

1.如果 referer-policy 的值为never:删除 http head 中的 referer 
例:
 <meta name="referrer" content="never">
2.如果 referer-policy 的值为default:如果当前页面使用的是 https 协议,而正要加载的资源使用的是普通的 http 协议,则将 http header 中的 referer 置为空
例:
 <meta name="referrer" content="default">
3.如果 referer-policy 的值为 origin:只发送 origin 部分
例:
<meta name="referrer" content="origin">
4.如果 referer-policy 的值为 always:不改变http header 中的 referer 的值,注意:这种情况下,如果当前页面使用了 https 协议,而要加载的资源使用的是 http 协议,加载资源的请求头中也会携带 referer
例:
<meta name="referrer" content="always">   

不过https跳转http会有refer头部丢失问题 由于 http规定 确实请求头没有Referer头部,原来是因为HTTP协议规定:

Clients SHOULD NOT include a Referer header field in a (non-secure) HTTP request if the referring page was transferred with a secure protocol.

手动输入网址也会丢失refer

不要把Rerferer用在身份验证或者其他非常重要的检查上,因为Rerferer非常容易在客户端被改变。

参考 Meta referrer in wiki
参考 译文

在html的结构中 head中有meta标签中的 其中meta标签有viewport属性 如下

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
width:控制 viewport 的大小,可以指定的一个值,如果 600,或者特殊的值,如 device-width 为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。
height:和 width 相对应,指定高度。
initial-scale:初始缩放比例,也即是当页面第一次 load 的时候缩放比例。
maximum-scale:允许用户缩放到的最大比例。
minimum-scale:允许用户缩放到的最小比例。
user-scalable:用户是否可以手动缩放
总的来说viewport就是控制用户是否能在移动端放大或者缩小的一个meta属性 有一些允许最大缩放比例和最小缩放比例

接下来就要说说移动端手机像素的问题
1.设备像素 指的是设备中使用的物理像素(Physic pixel)。这个单位用px表示,它是一个[相对绝对单位]:

即在同样一个设备上,每1个设备像素所代表的物理长度(如英寸)是固定不变的(即设备像素的绝对性);
但在不同的设备之间,每1个设备像素所代表的物理长度(如英寸)是可以变化的(即设备像素的相对性);

2.what?说的是在不同设备之间 设备像素是变化的?那么 这有给前端开发工程师带来了难题 zzzzzzz 接下来说明一下大家买手机 介绍总有什么什么的分辨率 譬如 1920*1080p的 那么 根据公式 ppi = 1920的平方乘以1080的平方 除以 屏幕尺寸 屏幕尺寸就是手机的尺寸 买手机的参数会有说明 举个栗子就明白了 5英寸 5.5英寸 5.7英寸 这些数字应该很熟悉吧 zzzzzzzzz

讲了半天 ppi有什么用呢 专业的话说 用于度量计算机显示屏上像素的密度 即每英寸屏幕所拥有的像素数 例如魅族手机 有500dpi(瞎写的)
就是一英尺能有500个像素 dpi越大 一英尺所容纳的像素越多 则字越小 zzzzzzzzzzzz 但是又想在不同手机中展示相同的尺寸 怎么办呢

3.我能想到的就是在移动端用rem 可能有更好地方法 但是我感觉我这方法简单定义好body字体 然后呢? 我使用css预编译语言stylus
带个张鑫旭老师stylus文档传送门
定义一个mixin

rem($val) 
  $val/$default rem

说了这么多 解决方法就是这么简单zzzzzzzzzz 哈哈

--end

下面一行代码

var value = 1;
function foo() {
    console.log(value);
}
function bar() {
    var value = 2;
    foo();
}
bar();  //结果是?

因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。
而与词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。
如果是动态作用域 就会调用函数的作用域 则答案是 2
答案 是 1
参考 伢羽:JavaScript深入之词法作用域和动态作用域

之前写了关于requirejs源码分析 想必对浏览器模块化原理有一定理解
那这次就对服务器端node的commonJS进行源码分析 先来讲讲关于require
服务器端的require是运行时加载 这个和es6的import不是一样的 es6的import是编译时加载 现在来分析require的字符串格式
require可以加载自己的核心模块 第三方模块 js文件 json文件 以及编译好的c++模块 (.node结尾的)
加载顺序是 :

1.先加载核心模块 比如 require("http");
2.试图在require的名称后面加上.js去搜索并加载
3. 试图在require的名称后面加上.json搜索并加载 列如 package.json  
4.试图在后面加上.node搜索编译好的c++模块   

看一下require的查找

require(X) from module at path Y//请求模块x在路径y
1. If X is a core module,   //如果x是核心模块
   a. return the core module
   b. STOP
2. If X begins with '/'   如果x是开始/
   a. set Y to be the filesystem root /在项目根路径找  可能会找到“package.json"
3. If X begins with './' or '/' or '../'  如果X开始
    ./或/或../
   a. LOAD_AS_FILE(Y + X)  加载文件(x+y)后缀名依照上面顺序
   b. LOAD_AS_DIRECTORY(Y + X) 或者找(x+y)的文件夹
4. LOAD_NODE_MODULES(X, dirname(Y))  //找系统目录下的node_modules
5. THROW "not found" //如果没有找到  抛出 not found

LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text.  STOP
2. If X.js is a file, load X.js as JavaScript text.  STOP
3. If X.json is a file, parse X.json to a JavaScript Object.  STOP
4. If X.node is a file, load X.node as binary addon.  STOP

LOAD_INDEX(X)  //找文件夹的index
1. If X/index.js is a file, load X/index.js as JavaScript text.  STOP
2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
3. If X/index.node is a file, load X/index.node as binary addon.  STOP

LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
   a. Parse X/package.json, and look for "main" field.
   b. let M = X + (json main field)
   c. LOAD_AS_FILE(M)
   d. LOAD_INDEX(M)
2. LOAD_INDEX(X)

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)  //找到项目路径的node_modules
2. for each DIR in DIRS:
   a. LOAD_AS_FILE(DIR/X)
   b. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
   a. if PARTS[I] = "node_modules" CONTINUE
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIRS + DIR
   d. let I = I - 1
5. return DIRS  

官网 有句话这么在查找node_modules中说的 例require(“express”)

If it is not found there, then it moves to the parent directory, and so on, until the root of the file system is reached.
然后操作系统的node_modules中找

参考node的modules的api
总结来说require的加载顺序 区分为带/和不带/的两种方式 先找缓存区
带/的就是找路径
如果不带/的 就先在核心模块找 如果是 stop 然后在项目的node_module
但是,只要首次加载成功后,node就会缓存起来,它缓存的是编译后的二进制模块,保证以后的加载速度 看一下源码注释 zzzzzzzzzz

// Check the cache for the requested file.
 // 1. If a module already exists in the cache: return its exports object.
 // 2. If the module is native: call `NativeModule.require()` with the
 //    filename and return the result.
 // 3. Otherwise, create a new module for the file and save it to the cache.
 //    Then have it load  the file contents before returning its exports
 //    object. 

module的源码地址

在前端的路上的时间越来越长了,越学感觉自己学的越少,需要学的也越多,看过许多大神的经历,总结来说 就是”越努力 越幸运” 春招过去了 自己虽然没有任何面试的经历 看到旁边的同学 一个个走出去 虽然心也是很着急 但是也是也是很无奈 虽然现在也投了一些简历 不是自己不够强 而是别人比你强 不是自己不合适 而是别人比你合适 (只能这样安慰自己了zzzzzzz)

浅谈requireJS源码 版本 2.3.3
开始是这样的
作者定义一个立即执行函数 有两个参数 global 和 setTimeOut
匿名函数开始是作者自己结合原生js定义的一些方法
例如:

function isFunction(it) {
return ostring.call(it) === ‘[object Function]’;
}
ostring是什么? 原来是 Object.prototype.toString

大致流程是
newContext函数开始
设置handlers对象
设置Module类
设置context对象
newContext函数完 返回值context对象
设置req函数
执行req({})
设置define函数
最后再次执行req({cfg})函数

-> newContext 中的部分代码 代码太多 不罗列
开始定义一堆属性 然后定义一下内部要用到的函数 设置handlers对象

有三个属性值 require  exports  module  

-> 设置Module类 模块类

-> 设置context对象

关键函数  configure 主要设置cfg的属性   makeShimExports函数  对shime进行特殊处理(即加载特殊要提前依赖的模块)
makerequir函数 定义加载的依赖模块函数   
其中调用mix混合对象属性  
nameToUrl函数 将模块名转换为路径 
load函数  调用req的load
 completeLoad函数 完成加载函数  
onScriptLoad函数  检查加载状态 如果加载完成 
interactiveScript的值置为null   继续调用completeLoad函数  completeLoad继续调用takeGlobalQueue 
onScriptError 检查script标签错误
好像    少了点什么  诶   checkLoaded  检查加载的 
在completeLoad函数中的最后调用checkLoaded
看一下核心函数 
 completeLoad: function (moduleName) {  //接受参数模块名称
        var found, args, mod,
            shim = getOwn(config.shim, moduleName) || {},
            shExports = shim.exports;
        takeGlobalQueue();    //推入队列
        while (defQueue.length) {
            args = defQueue.shift();
            if (args[0] === null) {
                args[0] = moduleName;
                if (found) {
                    break;
                }
                found = true;
            } else if (args[0] === moduleName) {
                found = true;
            }

            callGetModule(args);
        }
        context.defQueueMap = {};
        mod = getOwn(registry, moduleName);

        if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) {
            if (config.enforceDefine && (!shExports || !getGlobal(shExports))) {
                if (hasPathFallback(moduleName)) {
                    return;
                } else {
                    return onError(makeError('nodefine',
                                     'No define call for ' + moduleName,
                                     null,
                                     [moduleName]));
                }
            } else {
                callGetModule([moduleName, (shim.deps || []), shim.exportsFn]);
            }
        }

        checkLoaded();
    }

-> 核心函数req 接受四个参数 模块名数组 执行函数 错误处理 选项

 req = requirejs = function(deps,callback,errback,optional){...}
 if (!isArray(deps) && typeof deps !== 'string') {  //如果第一个参数不是数组 是一个定义对象 
      config = deps;       
      if (isArray(callback)) {
          deps = callback;
          callback = errback;
          errback = optional;
      } else {
          deps = [];
      }
  }
  if (config && config.context) {    
      contextName = config.context;
  }
  context = getOwn(contexts, contextName);
  if (!context) {
      context = contexts[contextName] = req.s.newContext(contextName);
  }
  if (config) {
      context.configure(config);
  }
  return context.require(deps, callback, errback);  //调用context的require  间接调用context 的makeRequire  返回闭包函数 localRequire 然后调用  zzzzzzzzz  执行 intakeDefines()函数  intakeDefines执行 takeGlobalQueue 将globalDefQueue push进context的队列 然后置为空数组
          /* 检测 入口 data-main */  
if (isBrowser && !cfg.skipDataMain) {  //判断是否是浏览器环境          isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),
  eachReverse(scripts(), function (script) {
      if (!head) {
          head = script.parentNode;
      } dataMain = script.getAttribute('data-main');
      if (dataMain) {
      if (!cfg.baseUrl && mainScript.indexOf('!') === -1) {
              src = mainScript.split('/');
              mainScript = src.pop();
              subPath = src.length ? src.join('/')  + '/' : './';
              cfg.baseUrl = subPath;
          }
          mainScript = mainScript.replace(jsSuffixRegExp, '');
          if (req.jsExtRegExp.test(mainScript)) {
              mainScript = dataMain;
          }    
          cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];   //将data-main的属性值推入dep中
          return true;
      }
  });

define函数

requireJS作者是这样定义的

define = function(name,deps,callback){…}
查阅官方api define有三种写法
第一种 包含三个参数 第一个参数是 模块的名称 第二个是引入的模块
第三个是 执行的函数
第二种 包含两个参数 第一个参数 是要引入的模块 第二个是要执行的函数
第三种 包含一个参数 就是执行的函数
源码是这样写的 外加我的注释

  /*  参数逻辑判断  */
if (typeof name !== 'string') {   //判断第一个参数是否是字符串 如果不是
        callback = deps;   //后面的参数向前移一位
        deps = name;
        name = null;
    }
    if (!isArray(deps)) {    //deps要引入模块是否是数组  如果不是  
        callback = deps;      //后面的参数前移
        deps = null;
    }
    /*如果传入参数中没有数组*/
    if (!deps && isFunction(callback)) {   
        deps = [];
        if (callback.length) {  //  判断callback实际接收的参数
            callback
                .toString()  // 将可执行函数转换为字符串
                .replace(commentRegExp, commentReplace) /* 这里是个正则匹配 commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/mg 用来过滤注释用的*/
                .replace(cjsRequireRegExp, function (match, dep) {
                    deps.push(dep);  //将require字段引入的push到dep中   
                });
            deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps);
        }
    }
 if (useInteractive) { //useInteractive在文初被设为false  这个应该是判断是IE(6-8)script标签做的特殊处理  (getInteractiveScript函数)ie6-8不支持script标签 支持onreadystatechange属性 有“uninitialized” – 原始状态 “loading” – 下载数据中..“loaded” – 下载完成 “interactive” – 还未执行完毕. “complete” – 脚本执行完毕.
        node = currentlyAddingScript || getInteractiveScript();
        if (node) {
            if (!name) {
                name = node.getAttribute('data-requiremodule'); //获得引入模块的名字
            }
            context = contexts[node.getAttribute('data-requirecontext')]; //找出模块中的名字
        }
    }  
         if (context) {             //将此模块加入队列中 
        context.defQueue.push([name, deps, callback]);    
        context.defQueueMap[name] = true;
    } else {
        globalDefQueue.push([name, deps, callback]);
    }

看一下执行代码

req.exec = function (text) {     /*将string文本eval  zzzzzzzzzzz*/
    return eval(text);
};

触发是 req.load这个函数开始执行 第二个参数是主要参数 即moduleName context.completeLoad(moduleName)
模块加载完成执行 onScriptLoad 然后 再次执行completeLoad然后一层层加载 -end

–转载声明出处